
package VideoRent.Entrada;

import java.io.BufferedReader;
import java.util.regex.*;
import java.util.HashSet;

import VideoRent.*;
import VideoRent.Enum.*;
import VideoRent.Articulo.*;

public class ArticulosExistencia extends VideoRentEntrada
{
    //Elementos de la tienda donde se registran los nuevos clientes
    private ElementosTienda elementosTienda;
    //Expresión regular que analiza la cabecera de los artículos en existencia
    private Pattern patronCabecera = null;
    //Expresión regular que analiza una película
    private Pattern patronPelicula = null;
    //Expresión regular que analiza una temporada
    private Pattern patronTemporada = null;
    //Expresión regular que analiza un juego recreativo
    private Pattern patronJuegoRecreativo = null;
    //Expresión regular que analiza un juego educativo
    private Pattern patronJuegoEducativo = null;


    /**
     * Constructor del lector de artículos existentes
     */
    public ArticulosExistencia(ElementosTienda elementosTienda){
        this.elementosTienda = elementosTienda;
    }


    /**
     * Lee todo el contenido de un archivo de artículos existentes y lo carga
     * a los elementos de la tienda. Realiza las debidas validaciones sobre
     * el formato del archivo.
     * 
     * @param bufferLectura Buffer del archivo que contiene los artículos existentes
     */
    public void leer(BufferedReader bufferLectura)
        throws VideoRentEntradaException
    {
        String linea = leerLinea(bufferLectura);
        if (null == linea){
            throw new VideoRentEntradaException("Archivo mal formateado: no vino la primera linea con la cantidad de articulos");
        }

        Matcher buscadorCabecera = getPatronCabecera().matcher(linea);

        if (!buscadorCabecera.find()){
            throw new VideoRentEntradaException("Archivo mal formateado: primera línea en formato incorrecto '" + linea + "'");
        }

        int totalArticulosExistentes;
        try{
            totalArticulosExistentes = Integer.parseInt(buscadorCabecera.group(1));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Total de articulos existentes inválido: '" + buscadorCabecera.group(1) + "'");
        }

        for (int i = 0; i < totalArticulosExistentes; ++ i){
            linea = leerLinea(bufferLectura);
            if (null == linea){
                throw new VideoRentEntradaException("Se esperaban '" + totalArticulosExistentes + "' articulos. Se encontraron '" + i + "'");
            }

            Matcher buscador;
            buscador = getPatronPelicula().matcher(linea);
            if (buscador.find()){
                agregarPelicula(buscador);
                continue;
            }

            buscador = getPatronTemporada().matcher(linea);
            if (buscador.find()){
                agregarTemporada(buscador);
                continue;
            }

            buscador = getPatronJuegoRecreativo().matcher(linea);
            if (buscador.find()){
                agregarJuegoRecreativo(buscador);
                continue;
            }

            buscador = getPatronJuegoEducativo().matcher(linea);
            if (buscador.find()){
                agregarJuegoEducativo(buscador);
                continue;
            }

            throw new VideoRentEntradaException("La línea '" + linea + "' no corresponde con la expresión regular de ningún artículo");
        }
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * la cabecera. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza la cabecera
     */
    protected Pattern getPatronCabecera(){
        if (null == patronCabecera){
            patronCabecera = Pattern.compile("^(\\d+)$");
        }
        return patronCabecera;
    }


    /**
     * Dada una coincidencia de expresión regular de película,
     * formatea la información y genera una película. Realiza validaciones
     * de unicidad y semántica.
     * 
     * @param buscadorPelicula Objeto que contiene la coincidencia de línea y
     *                         los resultados de la información de la película
     */
    protected void agregarPelicula(Matcher buscadorPelicula)
        throws VideoRentEntradaException
    {

        if (null != elementosTienda.buscarArticulo(buscadorPelicula.group(1))){
            throw new VideoRentEntradaException("La película con el código '" + buscadorPelicula.group(1) + "' ya fue asociado");
        }

        int cantidad;
        try{
            cantidad = Integer.parseInt(buscadorPelicula.group(2));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cantidad en existencia inválida: '" + buscadorPelicula.group(2) + "' para la película '" + buscadorPelicula.group(1) + "'");
        }

        int ano;
        try{
            ano = Integer.parseInt(buscadorPelicula.group(4));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Año inválido: '" + buscadorPelicula.group(4) + "' para la película '" + buscadorPelicula.group(1) + "'");
        }

        GeneroAudiovisual genero;
        String generoString = buscadorPelicula.group(5).toLowerCase();
        if ("accion".equals(generoString)){
            genero = GeneroAudiovisual.ACCION;
        }else if("comedia".equals(generoString)){
            genero = GeneroAudiovisual.COMEDIA;
        }else if("drama".equals(generoString)){
            genero = GeneroAudiovisual.DRAMA;
        }else if("fantasia".equals(generoString)){
            genero = GeneroAudiovisual.FANTASIA;
        }else if("terror".equals(generoString)){
            genero = GeneroAudiovisual.TERROR;
        }else if("suspenso".equals(generoString)){
            genero = GeneroAudiovisual.SUSPENSO;
        }else{
            throw new VideoRentEntradaException("Género de película inválido: '" + buscadorPelicula.group(5) + "' para la película '" + buscadorPelicula.group(1) + "'");
        }

        FormatoPelicula formato;
        String formatoString = buscadorPelicula.group(6).toLowerCase();
        if ("dvd".equals(formatoString)){
            formato = FormatoPelicula.DVD;
        }else if("vhs".equals(formatoString)){
            formato = FormatoPelicula.VHS;
        }else if("blu-ray".equals(formatoString)){
            formato = FormatoPelicula.BLU_RAY;
        }else{
            throw new VideoRentEntradaException("Formato de película inválido: '" + buscadorPelicula.group(6) + "' para la película '" + buscadorPelicula.group(1) + "'");
        }

        Pelicula secuela = null;
        if (null != buscadorPelicula.group(10)){
            secuela = (Pelicula) elementosTienda.buscarArticulo(buscadorPelicula.group(10));
            if (null == secuela){
                throw new VideoRentEntradaException("Secuela inválida: '" + buscadorPelicula.group(10) + "' para la película '" + buscadorPelicula.group(1) + "'");
            }
        }

        elementosTienda.agregarArticulo(new Pelicula(
            //Código
            buscadorPelicula.group(1),
            //Nombre
            buscadorPelicula.group(3), cantidad, ano,
            //Actores
            buscadorPelicula.group(9), genero, formato,
            //Escritores
            buscadorPelicula.group(8),
            //Directores
            buscadorPelicula.group(7),
            secuela));
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * una línea de película. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza la película
     */
    protected Pattern getPatronPelicula(){
        if (null == patronPelicula){
            patronPelicula = Pattern.compile(
                //Código
                "^(P\\d+)\\s*" +
                //Cantidad en existencia
                "&\\s*(\\d+)\\s*" +
                //Nombre
                "&\\s*([^&]+?)\\s*" +
                //Año
                "&\\s*([^&]+?)\\s*" +
                //Género
                "&\\s*([^&]+?)\\s*" +
                //Formato
                "&\\s*([^&]+?)\\s*" +
                //Directores
                "&\\s*([^&]+?)\\s*" +
                //Escritores
                "&\\s*([^&]+?)\\s*" +
                //Actores
                "&\\s*([^&]+?)\\s*" +
                //Secuela
                "(?:&\\s*(P\\d+)\\s*)?$");
        }
        return patronPelicula;
    }


    /**
     * Dada una coincidencia de expresión regular de temporada,
     * formatea la información y genera una temporada. Realiza validaciones
     * de unicidad y semántica.
     * 
     * @param buscadorTemporada Objeto que contiene la coincidencia de línea y
     *                          los resultados de la información de la temporada
     */
    protected void agregarTemporada(Matcher buscadorTemporada)
        throws VideoRentEntradaException
    {

        if (null != elementosTienda.buscarArticulo(buscadorTemporada.group(1))){
            throw new VideoRentEntradaException("La temporada con el código '" + buscadorTemporada.group(1) + "' ya fue asociado");
        }

        int cantidad;
        try{
            cantidad = Integer.parseInt(buscadorTemporada.group(2));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cantidad en existencia inválida: '" + buscadorTemporada.group(2) + "' para la temporada '" + buscadorTemporada.group(1) + "'");
        }

        int numero;
        try{
            numero = Integer.parseInt(buscadorTemporada.group(4));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Número de temporada inválido: '" + buscadorTemporada.group(4) + "' para la temporada '" + buscadorTemporada.group(1) + "'");
        }

        int cantidadEpisodios;
        try{
            cantidadEpisodios = Integer.parseInt(buscadorTemporada.group(8));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cantidad de episodios inválida: '" + buscadorTemporada.group(8) + "' para la temporada '" + buscadorTemporada.group(1) + "'");
        }

        int ano;
        try{
            ano = Integer.parseInt(buscadorTemporada.group(5));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Año inválido: '" + buscadorTemporada.group(5) + "' para la temporada '" + buscadorTemporada.group(1) + "'");
        }

        elementosTienda.agregarArticulo(new Temporada(
            //Código
            buscadorTemporada.group(1),
            //Nombre
            buscadorTemporada.group(3), cantidad,
            //Actores
            buscadorTemporada.group(7), numero, cantidadEpisodios,
            //Creadores
            buscadorTemporada.group(6), ano));
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * una línea de temporada. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza la temporada
     */
    protected Pattern getPatronTemporada(){
        if (null == patronTemporada){
            patronTemporada = Pattern.compile(
                //Código
                "^(S\\d+)\\s*" +
                //Cantidad en existencia
                "&\\s*(\\d+)\\s*" +
                //Nombre
                "&\\s*([^&]+?)\\s*" +
                //Número Temporada
                "&\\s*(\\d+)\\s*" +
                //Año
                "&\\s*(\\d+)\\s*" +
                //Creadores
                "&\\s*([^&]+?)\\s*" +
                //Actores
                "&\\s*([^&]+?)\\s*" +
                //Cantidad de episodios
                "&\\s*(\\d+)\\s*$");
        }
        return patronTemporada;
    }


    /**
     * Dada una coincidencia de expresión regular de juego recreativo,
     * formatea la información y genera un juego recreativo. Realiza validaciones
     * de unicidad y semántica.
     * 
     * @param buscadorJuegoRecreativo Objeto que contiene la coincidencia de línea y
     *                                los resultados de la información del juego recreativo
     */
    protected void agregarJuegoRecreativo(Matcher buscadorJuegoRecreativo)
        throws VideoRentEntradaException
    {

        if (null != elementosTienda.buscarArticulo(buscadorJuegoRecreativo.group(1))){
            throw new VideoRentEntradaException("El juego recreativo con el código '" + buscadorJuegoRecreativo.group(1) + "' ya fue asociado");
        }

        int cantidad;
        try{
            cantidad = Integer.parseInt(buscadorJuegoRecreativo.group(2));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cantidad en existencia inválida: '" + buscadorJuegoRecreativo.group(2) +
                "' para el juego recreativo '" + buscadorJuegoRecreativo.group(1) + "'");
        }

        GeneroJuego genero;
        String generoString = buscadorJuegoRecreativo.group(4).toLowerCase();
        if ("aventura".equals(generoString)){
            genero = GeneroJuego.AVENTURA;
        }else if("accion".equals(generoString)){
            genero = GeneroJuego.ACCION;
        }else if("deportes".equals(generoString)){
            genero = GeneroJuego.DEPORTES;
        }else if("estrategia".equals(generoString)){
            genero = GeneroJuego.ESTRATEGIA;
        }else{
            throw new VideoRentEntradaException("Género de juego recreativo inválido: '" + buscadorJuegoRecreativo.group(4) +
                "' para el juego '" + buscadorJuegoRecreativo.group(1) + "'");
        }

        int ano;
        try{
            ano = Integer.parseInt(buscadorJuegoRecreativo.group(7));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Año inválido: '" + buscadorJuegoRecreativo.group(7) +
                "' para el juego '" + buscadorJuegoRecreativo.group(1) + "'");
        }

        elementosTienda.agregarArticulo(new JuegoRecreativo(
            //Código
            buscadorJuegoRecreativo.group(1),
            //Nombre
            buscadorJuegoRecreativo.group(3), cantidad,
            //Plataforma
            buscadorJuegoRecreativo.group(5),
            //Desarrollador
            buscadorJuegoRecreativo.group(6), genero, ano));
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * una línea de juego recreativo. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza el juego recreativo
     */
    protected Pattern getPatronJuegoRecreativo(){
        if (null == patronJuegoRecreativo){
            patronJuegoRecreativo = Pattern.compile(
                //Código
                "^(R\\d+)\\s*" +
                //Cantidad en existencia
                "&\\s*(\\d+)\\s*" +
                //Nombre
                "&\\s*([^&]+?)\\s*" +
                //Género
                "&\\s*([^&]+?)\\s*" +
                //Plataforma
                "&\\s*([^&]+?)\\s*" +
                //Creador
                "&\\s*([^&]+?)\\s*" +
                //Año
                "&\\s*(\\d+)\\s*$");
        }
        return patronJuegoRecreativo;
    }


    /**
     * Dada una coincidencia de expresión regular de juego educativo,
     * formatea la información y genera un juego educativo. Realiza validaciones
     * de unicidad y semántica.
     * 
     * @param buscadorJuegoEducativo Objeto que contiene la coincidencia de línea y
     *                                los resultados de la información del juego educativo
     */
    protected void agregarJuegoEducativo(Matcher buscadorJuegoEducativo)
        throws VideoRentEntradaException
    {

        if (null != elementosTienda.buscarArticulo(buscadorJuegoEducativo.group(1))){
            throw new VideoRentEntradaException("El juego educativo con el código '" + buscadorJuegoEducativo.group(1) + "' ya fue asociado");
        }

        int cantidad;
        try{
            cantidad = Integer.parseInt(buscadorJuegoEducativo.group(2));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cantidad en existencia inválida: '" + buscadorJuegoEducativo.group(2) +
                "' para el juego educativo '" + buscadorJuegoEducativo.group(1) + "'");
        }

        Destreza destreza;
        String destrezaString = buscadorJuegoEducativo.group(4).toLowerCase();
        if ("aventura".equals(destrezaString)){
            destreza = Destreza.VERBAL;
        }else if("numerica".equals(destrezaString)){
            destreza = Destreza.NUMERICA;
        }else if("deportes".equals(destrezaString)){
            destreza = Destreza.ESPACIAL;
        }else{
            throw new VideoRentEntradaException("Destreza de juego educativo inválida: '" + buscadorJuegoEducativo.group(4) + "' para el juego '" + buscadorJuegoEducativo.group(1) + "'");
        }

        int ano;
        try{
            ano = Integer.parseInt(buscadorJuegoEducativo.group(6));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Año inválido: '" + buscadorJuegoEducativo.group(6) + "' para el juego '" + buscadorJuegoEducativo.group(1) + "'");
        }

        elementosTienda.agregarArticulo(new JuegoEducativo(
            //Código
            buscadorJuegoEducativo.group(1),
            //Nombre
            buscadorJuegoEducativo.group(3), cantidad,
            //Plataforma
            buscadorJuegoEducativo.group(5), destreza, ano));
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * una línea de juego educativo. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza el juego educativo
     */
    protected Pattern getPatronJuegoEducativo(){
        if (null == patronJuegoEducativo){
            patronJuegoEducativo = Pattern.compile(
                //Código
                "^(E\\d+)\\s*" +
                //Cantidad en existencia
                "&\\s*(\\d+)\\s*" +
                //Nombre
                "&\\s*([^&]+?)\\s*" +
                //Destreza
                "&\\s*([^&]+?)\\s*" +
                //Plataforma
                "&\\s*([^&]+?)\\s*" +
                //Año
                "&\\s*(\\d{4})\\s*$");
        }
        return patronJuegoEducativo;
    }
}